package main

import (
	"fmt"
	"math"
	"unicode/utf8"
)

/**
 * 变量的基本使用
 */
var a = "hello world"
var b string = "golang"
var c bool

func main() {

	fmt.Println("start : 包级常量的使用")
	fmt.Print(a, b, c)
	fmt.Println("end: 包级常量的使用")

	fmt.Println("start : 局部变量的使用")
	// 常量
	const LENGTH = 10
	const WIDTH = 5

	var area int

	// 多重赋值
	const a0, b0, c0 = 1, false, "str"

	area = LENGTH * WIDTH
	fmt.Println()
	fmt.Printf("area : %d\n", area)
	fmt.Print(a0, b0, c0)

	fmt.Println("end : 局部变量的使用")

	// 常量组
	const (
		Unknown = 0
		Female  = 1
		Male    = 2
	)

	fmt.Println("start : 特殊变量 (iota) 的使用 ")
	// iota，特殊常量，可以认为是一个可以被编译器修改的常量
	/**
	 * 第一个 iota 等于 0，每当 iota 在新的一行被使用时，它的值都会自动加 1；
	 * 所以 a=0, b=1, c=2 可以简写为如下形式：
	 */
	const (
		a = iota
		b
		c
	)

	fmt.Println("end : 特殊变量 (iota) 的使用 ")

	// 类型打印
	fmt.Println("start : 类型打印 ")
	d := "Hello World" //string
	e := "Ruby"        //string
	f := 'A'
	g := 100  //int
	h := 3.14 //float64
	i := true // bool

	fmt.Println("start : 输出类型与数值")
	fmt.Printf("%T,%s\n", d, d)
	fmt.Printf("%T,%s\n", e, e)
	fmt.Printf("%T,%d,%c\n", f, f, f)
	fmt.Printf("%T,%b\n", g, g)
	fmt.Printf("%T,%f\n", h, h)
	fmt.Printf("%T,%t\n", i, i)
	fmt.Println("end : 输出类型与数值")

	fmt.Println("start : 输出数值")
	fmt.Printf("%v\n", d)
	fmt.Printf("%v\n", e)
	fmt.Printf("%v\n", f)
	fmt.Printf("%v\n", g)
	fmt.Printf("%v\n", h)
	fmt.Printf("%v\n", i)
	fmt.Println("end : 输出数值")

	fmt.Println("end: 类型打印 ")

	// 浮点数的基本使用
	fmt.Println("start: go浮点数的基本使用")
	// float32
	var f1 float32 = 16777216.0
	var f2 float32 = 16777217.0
	// 为什么相等? 因为转为二进制相等，丢失精度了
	fmt.Println(f1 == f2) // true

	f1Bit, f2Bit := math.Float32bits(f1), math.Float32bits(f2)

	// 1001011100000000000000000000000
	fmt.Printf("float32: f1的二进制数据为 %b\n", f1Bit)
	// 1001011100000000000000000000000
	fmt.Printf("float32: f2的二进制数据为 %b\n", f2Bit)

	// float64
	var f11 float64 = 16777216.0
	var f22 float64 = 16777217.0

	// 不相等了
	fmt.Println(f11 == f22) // false

	f11Bit, f22Bit := math.Float64bits(f11), math.Float64bits(f22)
	// 100000101110000000000000000000000000000000000000000000000000000
	fmt.Printf("float64: f11的二进制数据为 %b\n", f11Bit)
	// 100000101110000000000000000000000010000000000000000000000000000
	fmt.Printf("float64: f22的二进制数据为 %b\n", f22Bit)

	fmt.Println("end: go浮点数的基本使用")

	// 复数的基本使用
	fmt.Println("start: go复数的基本使用")
	var c1 = 5 + 6i
	var c2 = complex(5, 6)

	realPart := real(c2)
	imagPart := imag(c2)

	fmt.Printf("c1复数输出 %T, %v \n", c1, c1)
	fmt.Printf("c2复数输出 %T, %v \n", c2, c2)

	fmt.Printf("c2复数实部: %v,虚部: %v \n", realPart, imagPart)

	fmt.Println("end: go复数的基本使用")

	// go 字符串的基本使用
	/**
	 * string 类型其实是一个描述符，它本身并不真正存储字符串数据，而仅是由一个指向底层存储的指针和字符串的长度字段组成的。
	 * Go 编译器把源码中的 string 类型映射为运行时的一个二元组（Data, Len），真实的字符串值数据就存储在一个被 Data 指向的底层数组中。
	 */
	fmt.Println("start: go字符串的基本使用")
	var s = "中国人"
	// 输出 字节序列长度为 9
	fmt.Printf("the length of s=%d\n", len(s))

	// 输出字符的长度 3
	fmt.Printf("the character length of s=%d\n", utf8.RuneCountInString(s))

	// 字符串下标操作
	fmt.Printf("0x%x\n", s[0]) // 0xe4：字符“中” utf-8编码的第一个字节

	// 字符迭代 普通迭代
	for i := 0; i < len(s); i++ {
		// 0xe4 0xb8 0xad 0xe5 0x9b 0xbd 0xe4 0xba 0xba
		fmt.Printf("index: %d, value: 0x%x\n", i, s[i])
	}
	fmt.Println()

	// 输出分别对应 中国人这三个字符 for range
	/**
	*  for range 迭代，每轮迭代得到的是字符串中 Unicode 字符的码点值，以及该字符在字符串中的偏移值。
	*   可以通过这样的迭代，获取字符串中的字符个数，而通过 Go 提供的内置函数 len，只能获取字符串内容的长度（字节个数）。
	*   获取字符串中字符个数更专业的方法，是调用标准库 UTF-8 包中的 RuneCountInString 函数
	 */
	for i, c := range s {
		fmt.Printf("index: %d, value: 0x%x\n", i, c) // 0x4e2d 0x56fd 0x4eba
	}
	fmt.Println()

	// 字符串连接, Go 还提供了 strings.Builder、strings.Join、fmt.Sprintf 等函数来进行字符串连接操作。
	s01 := "Rob Pike, "
	s01 = s01 + "Robert Griesemer, "
	s01 += " Ken Thompson"

	fmt.Println(s01) // Rob Pike, Robert Griesemer, Ken Thompson

	// 字符串比较
	s1 := "Go"
	s2 := "C"
	fmt.Println(s1 != s2) // false

	// 字符串转换
	// string -> []rune
	rs := []rune(s)
	fmt.Printf("%x \n", rs) // [4e2d 56fd 4eba]

	// string -> []byte
	bs := []byte(s)
	fmt.Printf("%x\n", bs) // e4b8ade59bbde4baba

	// []rune -> string
	s03 := string(rs)
	fmt.Println(s03)

	// []byte -> string
	s04 := string(bs)
	fmt.Println(s04)

	fmt.Println("end: go字符串的基本使用")

	// 常量的基本使用
	fmt.Println("start: go常量的基本使用")

	const (
		size    int64 = 4096
		m, n, l       = 13, 14, "bar"
	)

	// 实现枚举

	// 常量定义的后两行并没有被显式地赋予初始值，Go 编译器就为它们自动使用上一行的表达式
	const (
		Apple, Banana     = 11, 22
		Strawberry, Grape // 11, 12 使用上一行的初始化表达式
		Pear, Watermelon  // 11, 12 使用上一行的初始化表达式
	)

	// iota 是 Go 语言的一个预定义标识符，它表示的是 const 声明块（包括单行声明）中，每个常量所处位置在块中的偏移值（从零开始）
	const (
		// iota 的值是这行在 const 块中的偏移，iota 的值为 0，得到 mutexLocked 这个常量的值为 1 << 0，也就是 1。
		mutexLocked = 1 << iota
		// const 声明块中并没有显式的常量初始化表达式，根据 const 声明块里“隐式重复前一个非空表达式”的机制，
		//这一行就等价于 mutexWorken = 1 << iota。而且这一行是 const 块中的第二行，它的偏移量 iota 的值为 1，
		//得到 mutexWorken 这个常量的值为 1 << 1，也就是 2
		mutexWoken
		// 这个常量同 mutexWorken 一样，这一行等价于 mutexStarving = 1 << iota。而且，也因为这行的 iota 的值为 2，
		//可以得到 mutexStarving 这个常量的值为 1 << 2，也就是 4
		mutexStarving
		mutexWaiterShift      = iota
		starvationThresholdNs = 1e6
	)

	// 枚举值从 1 开始
	const (
		_           = iota
		IPV6_V6ONLY // 1
		SOMAXCONN   // 2
		SO_ERROR    // 3
	)
}
